package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.EntityMapperTeachPlan;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.ext.TeachPlanNodes;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private TeachplanMediaService teachplanMediaService;

    @Autowired
    private RedisTemplate<String, Serializable> redisTemlate;

    /*
      查询课程计划：
        1.判断关键字数据
               couseId，companyId是否为空
        2.判断业务数据
            课程基本信息：
                  判断是否存在
                  判断是否为同一家机构
                  判断是否为删除状态
        3.根据Courseid查询课程计划数据
    *   4.通过java的递归生成课程计划树形结构
    *   5.将数据转为dto并返回
     */
    @Override
    public TeachplanDTO selectTeachPlanTreeNodes(Long courseId, Long companyId) {
        //1.判断关键字数据
        //               couseId，companyId是否为空
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120019);
        }
        if (ObjectUtils.isEmpty(courseId)) {
            ExceptionCast.cast(ContentErrorCode.E_120022);
        }

        // 2.判断业务数据
        //            课程基本信息：
        //                  判断是否存在
        //                  判断是否为同一家机构
        //                  判断是否为删除状态
        CourseBase courseBase = courseBaseService.getById(courseId);
        if (ObjectUtils.isEmpty(courseBase)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }
        if (!companyId.equals(courseBase.getCompanyId())) {
            ExceptionCast.cast(ContentErrorCode.E_120020);
        }
        if (CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBase.getStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120112);
        }

        // 3.根据Courseid查询课程计划数据
        List<TeachPlanNodes> teachPlanNodes = baseMapper.selectTeachPlanNodes(courseId);
        TeachplanDTO teachplanDTO = null;
        if (ObjectUtils.isEmpty(teachPlanNodes)) {

            teachplanDTO = new TeachplanDTO();

        }else {

            TeachPlanNodes prarentNode = teachPlanNodes.remove(0);

            //4.通过java的递归生成课程计划树形结构
            getChilderNodes(prarentNode, teachPlanNodes);

            // 5.将数据转为dto并返回
            teachplanDTO = EntityMapperTeachPlan.INSTANCE.TeachplanToTeachplanDto(prarentNode);

        }

        return teachplanDTO;
    }
    /*
    递归添加子节点
    //4.通过java的递归生成课程计划树形结构
    */
    private void getChilderNodes(TeachPlanNodes prarentNode, List<TeachPlanNodes> nodes) {


        // 1.判断父级集合是否为空，如果为空需要创建
        if (CollectionUtils.isEmpty(prarentNode.getChildrenNodes())) {
            prarentNode.setChildrenNodes(new ArrayList<TeachPlanNodes>());
        }

        // 2.循环遍历集合生成树形结构
        for (TeachPlanNodes node : nodes) {

            // 2.1 判断node是否和parentnode有关联，如果有关联将node存放到parentNode集合中
            if (ObjectUtils.nullSafeEquals(prarentNode.getId(),node.getParentid())) {

                prarentNode.getChildrenNodes().add(node);

                // 2.2 生成node集合数据
                // 如果是第三级课程计划，无需递归
                if (!(TeachPlanEnum.THIRD_LEVEL.equals(node.getGrade()))) {
                    getChilderNodes(node,nodes);
                }
            }
        }
    }




    /*
         创建和删除业务分析：
            主体方法：
                1.判断业务操作
                    判断是创建还是修改：teachplanId
                    如果有：修改
                    如果无：创建

                2.执行创建和修改操作并返回dto数据

                3.返回结果数据dto给web
     */
    @Override
    @Transactional
    public TeachplanDTO createOrUpdateTeachPlan(TeachplanDTO dto, Long companyId) {

        TeachplanDTO resultDTO = null;
        if (ObjectUtils.isEmpty(dto.getTeachPlanId())) {
            resultDTO = createNode(dto, companyId);
        } else {
            resultDTO = updateNode(dto, companyId);
        }


        return resultDTO;
    }

    /*
        修改业务分析：
        1.判断关键数据
         2.判断业务数据
             课程基础信息
                 判断是否存在
                 判断是否同一家机构
                 判断是否删除
                 判断课程基础信息审核状态：未提交和审核未通过
             课程计划
                 判断是否存在
         3.修改课程计划数据
              pname starttime endtime is_preview
         4.判断修改后的结果
         5.将数据库最新的数据转为dto并返回
     */

    private TeachplanDTO updateNode(TeachplanDTO dto, Long companyId) {
        //1.判断关键数据
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120019);
        }
        if (ObjectUtils.isEmpty(dto.getCourseId())) {
            ExceptionCast.cast(ContentErrorCode.E_120022);
        }
        if (StringUtil.isBlank(dto.getPname())) {
            ExceptionCast.cast(ContentErrorCode.E_120415);
        }
        //         2.判断业务数据
        //             课程基础信息
        //                 判断是否存在
        //                 判断是否同一家机构
        //                 判断是否删除
        //                 判断课程基础信息审核状态：未提交和审核未通过
        //             课程计划
        //                 判断是否存在
        Long courseId = dto.getCourseId();
        CourseBaseDTO courseBaseDTO = courseBaseService.updatequeryById(courseId, companyId);
        if (ObjectUtils.isEmpty(courseBaseDTO)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }
        if (!companyId.equals(courseBaseDTO.getCompanyId())) {
            ExceptionCast.cast(ContentErrorCode.E_120020);
        }
        if (CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBaseDTO.getStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120112);
        }
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(courseBaseDTO.getAuditStatus())
                ||     CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(courseBaseDTO.getAuditStatus()))
        {
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }

        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getId, dto.getTeachPlanId());
        int count = this.count(queryWrapper);
        if (count < 1) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }

        //3.修改课程计划数据
        //     pname starttime endtime is_preview
        LambdaUpdateWrapper<Teachplan> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(Teachplan::getId, dto.getParentid())
                .set(Teachplan::getPname, dto.getPname())
                .set(Teachplan::getStartTime, dto.getStartTime())
                .set(Teachplan::getEndTime, dto.getEndTime())
                .set(Teachplan::getIsPreview, dto.getIsPreview());
        // 4.判断修改后的结果
        boolean update = this.update(lambdaUpdateWrapper);
        if (!update) {
            ExceptionCast.cast(ContentErrorCode.E_120419);
        }
        //         5.将数据库最新的数据转为dto并返回
        Teachplan newUpdateTeach = this.getById(dto.getTeachPlanId());

        TeachplanDTO resultDTO = EntityMapperTeachPlan.INSTANCE.TeachplanPoToTeachplanDto(newUpdateTeach);

        return resultDTO;
    }
    /*
        新建主体方法业务分析：
            1.判断关键数据
            2.判断业务数据
                课程基本信息
                    判断是否存在
                    判断是否为同一家机构
                    判断是否删除
                    判断基础课程的审核状态，未提交和未通过
             3.将dto转为po保存
            4.获得父级数据并进行关联
                    parentid
                    grade：子级grade时父级grade+1
                    orderby：获得父级课程计划的个数+1
             5.判断保存后的结果
             6.将数据库最新数据转为dto并返回web
     */

    private TeachplanDTO createNode(TeachplanDTO dto, Long companyId) {
        // 1.判断关键数据
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120019);
        }
        if (ObjectUtils.isEmpty(dto.getCourseId())) {
            ExceptionCast.cast(ContentErrorCode.E_120022);
        }
        if (StringUtil.isBlank(dto.getPname())) {
            ExceptionCast.cast(ContentErrorCode.E_120415);
        }

        // 2.判断业务数据
        //                课程基本信息
        //                    判断是否存在
        //                    判断是否为同一家机构
        //                    判断是否删除
        //                    判断基础课程的审核状态，未提交和未通过
        Long courseId = dto.getCourseId();
        CourseBaseDTO courseBaseDTO = courseBaseService.updatequeryById(courseId, companyId);
        if (ObjectUtils.isEmpty(courseBaseDTO)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }
        if (!companyId.equals(courseBaseDTO.getCompanyId())) {
            ExceptionCast.cast(ContentErrorCode.E_120020);
        }
        if (CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBaseDTO.getStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120112);
        }
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(courseBaseDTO.getAuditStatus())
           ||     CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(courseBaseDTO.getAuditStatus()))
        {
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }

        // 3.将dto转为po保存
        Teachplan teachplan = EntityMapperTeachPlan.INSTANCE.TeachplanDTOToTeachplanPo(dto);

        //            4.获得父级数据并进行关联
        //                    parentid
        //                    grade：子级grade时父级grade+1
        //                    orderby：获得父级课程计划的个数+1
        Teachplan parenNode = isParenNode(dto, courseBaseDTO);

        teachplan.setParentid(parenNode.getId());
        teachplan.setGrade(parenNode.getGrade()+1);
//        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(Teachplan::getParentid, parenNode.getId());
//        int count = this.count(queryWrapper);
//        teachplan.setOrderby(count+1);

        //使用redis来解决orderby次数问题
        Long increment = redisTemlate.opsForHash().increment(courseBaseDTO.getCourseBaseId().toString(), parenNode.getId() + ":" + parenNode.getGrade(), 1);
        teachplan.setOrderby(increment.intValue());

        //5.判断保存后的结果
        boolean save = this.save(teachplan);
        if (!save) {
            ExceptionCast.cast(ContentErrorCode.E_120417);
        }

        // 6.获取数据库最新数据转为dto并返回web
        Teachplan resultPo = this.getById(teachplan.getId());
        TeachplanDTO teachplanDTO = EntityMapperTeachPlan.INSTANCE.TeachplanPoToTeachplanDto(resultPo);

        return teachplanDTO;
    }
    /*
        新建业务：    获取父级资源
            一级资源操作二级资源时自动生成
            1.判断新增数据是二级还是三级课程计划
                   判断获得是2级或3级的父级数据：parentid
                        如果有：三级计划
                        如果额米有：二级计划
            2.获得二级课程计划的父级数据（一级数据）
                        查询数据库获得一级数据：courseId，parentid=0
                            如果为空，手动填写代码创建并保存数据--数据来源：coursebase
                            如果不为空：直接返回数据
            3.获得三级课程计划的父级数据（二级资源）
                   查询数据库获得二级课程计划：parentid
                        如果没有：直接抛出异常
                        如果有：直接返回

     */

    private Teachplan isParenNode(TeachplanDTO dto, CourseBaseDTO courseBaseDTO) {
        //1.判断新增数据是二级还是三级课程计划
        //     判断新增数据是否有parentId值
        //         如果有：三级课程计划
        //         如果没有：二级课程计划
        Long parentid = dto.getParentid();
        if (ObjectUtils.isEmpty(parentid)) {
            // 2.获得二级课程计划的父级数据
            //     查询数据库获得一级课程计划：courseId、parentid=0
            //         如果没有：自动创建并保存数据--数据来源 coursebase
            //         如果有：直接返回
            LambdaQueryWrapper<Teachplan> teachplanDTOQueryWrapper = new LambdaQueryWrapper<>();
            teachplanDTOQueryWrapper.eq(Teachplan::getCourseId, courseBaseDTO.getCourseBaseId());
            teachplanDTOQueryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);

            Teachplan rootteachplan = this.getOne(teachplanDTOQueryWrapper);

            //判断二级数据
            if (ObjectUtils.isEmpty(rootteachplan)) {
                rootteachplan = new Teachplan();
                rootteachplan.setCourseId(courseBaseDTO.getCourseBaseId());
                rootteachplan.setPname(courseBaseDTO.getName());
                rootteachplan.setGrade(TeachPlanEnum.FIRST_LEVEL);
                rootteachplan.setDescription(courseBaseDTO.getDescription());
                rootteachplan.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                rootteachplan.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                boolean save = this.save(rootteachplan);
                if (!save) {
                    ExceptionCast.cast(ContentErrorCode.E_120417);
                }
                return rootteachplan;
            } else {
                return rootteachplan;
            }
        } else {
            //有parentid  是三级数据
            LambdaQueryWrapper<Teachplan> QueryWrapper = new LambdaQueryWrapper<>();
            QueryWrapper.eq(Teachplan::getId, parentid);
            Teachplan thirdTeach = this.getOne(QueryWrapper);

            if (ObjectUtils.isEmpty(thirdTeach)) {
                ExceptionCast.cast(ContentErrorCode.E_120418);
            }
            return thirdTeach;
        }
    }


    /*
        删除业务操作：

	1.判断关键
		teachplanId  companyId
	2.判断业务数据
		课程基础信息
			判断是否存在、判断是否是同一家教学机构、判断是否删除、判断审核状态（未提交、审核未通过）
		课程计划
			判断删除的课程计划是几级数据
				二级
					判断是否存在子级数据，如果有不让删除
				三级
					如果是录播课程
						判断课程计划是否有关联数据（查询TeachplanMedia表数据）
     */
    @Override
    @Transactional
    public void deleteTeachPlan(Long teachplanId, Long companyId) {

        //1.判断关键
        //		teachplanId  companyId
        if (ObjectUtils.isEmpty(teachplanId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        //	2.判断业务数据
        //		课程基础信息
        //			判断是否存在、判断是否是同一家教学机构、判断是否删除、判断审核状态（未提交、审核未通过）
        //		课程计划
        //			判断删除的课程计划是几级数据
        //				二级
        //					判断是否存在子级数据，如果有不让删除
        //				三级
        //					如果是录播课程
        //						判断课程计划是否有关联数据（查询TeachplanMedia表数据）
        Teachplan teachplan = this.getById(teachplanId);
        CourseBase courseBase = courseBaseService.getById(teachplan.getCourseId());
        if (ObjectUtils.isEmpty(courseBase)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }
        if (!companyId.equals(courseBase.getCompanyId())) {
            ExceptionCast.cast(ContentErrorCode.E_120020);
        }
        if (CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBase.getStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120112);
        }
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(courseBase.getAuditStatus())
                ||     CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(courseBase.getAuditStatus()))
        {
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }


        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }

        //二级
        // 	判断是否存在子级数据，如果有不让删除
        if (TeachPlanEnum.SECEND_LEVEL.equals(teachplan.getGrade())) {
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getParentid, teachplanId);
            int count = this.count(queryWrapper);
            if (count > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120414);
            }
        }


        /*
                       三级
        					如果是录播课程
        					判断课程计划是否有关联数据（查询TeachplanMedia表数据）
         */
        if (TeachPlanEnum.THIRD_LEVEL.equals(teachplan.getGrade())) {
            LambdaQueryWrapper<TeachplanMedia> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(TeachplanMedia::getTeachplanId, teachplanId);
            int count = teachplanMediaService.count(wrapper);
            if (count > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120414);
            }
        }
        boolean b = this.removeById(teachplanId);
        if (!b) {
            ExceptionCast.cast(ContentErrorCode.E_120420);
        }
    }
}
